package com.xipu.yygh.order.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.wxpay.sdk.WXPayConstants;
import com.github.wxpay.sdk.WXPayUtil;
import com.xipu.yygh.common.exception.YyghException;
import com.xipu.yygh.enums.PaymentTypeEnum;
import com.xipu.yygh.enums.RefundStatusEnum;
import com.xipu.yygh.model.order.OrderInfo;
import com.xipu.yygh.model.order.PaymentInfo;
import com.xipu.yygh.model.order.RefundInfo;
import com.xipu.yygh.order.config.properties.WxPayProperties;
import com.xipu.yygh.order.service.OrderInfoService;
import com.xipu.yygh.order.service.PaymentInfoService;
import com.xipu.yygh.order.service.RefundInfoService;
import com.xipu.yygh.order.service.WeixinService;
import com.xipu.yygh.order.utils.HttpClient;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * description: 微信支付相关接口实现类
 * @author: gql
 * @date: 2022/05
 */
@Service
public class WeixinServiceImpl implements WeixinService {
    /**
     * 订单信息
     */
    @Autowired
    private OrderInfoService orderInfoService;

    /**
     * 支付信息
     */
    @Autowired
    private PaymentInfoService paymentInfoService;

    /**
     * Redis模板
     */
    @Autowired
    private RedisTemplate redisTemplate;


    /**
     * 微信退款
     */
    @Autowired
    private RefundInfoService refundInfoService;

    @Override
    public HashMap<String, Object> createNative(Long orderId) {
        try {
            // 0. 先查询redis中是否有支付信息,有就返回
            HashMap<String, Object> payMap = (HashMap<String, Object>) this.redisTemplate.opsForValue().get(String.valueOf(orderId));
            if (!CollectionUtils.isEmpty(payMap)) {
                return payMap;
            }

            // 1.查询订单信息
            OrderInfo orderInfo = this.orderInfoService.getOrderInfo(orderId);

            // 2.将订单信息添加到支付表中
            this.paymentInfoService.savePaymentInfo(orderInfo, PaymentTypeEnum.WEIXIN.getStatus());

            // 3.使用httpClient调用微信接口得到返回二维码地址等数据,再用Map封装返回数据
            Map<String, String> paramMap = new HashMap<>(16);
            paramMap.put("appid", WxPayProperties.APPID);
            paramMap.put("mch_id", WxPayProperties.PARTNER);
            // 每次请求传递一个随机数,确保都是新的请求,没什么意义,也可以自己生成
            paramMap.put("nonce_str", WXPayUtil.generateNonceStr());
            Date reserveDate = orderInfo.getReserveDate();
            String reserveDateString = new DateTime(reserveDate).toString("yyyy/MM/dd");
            String body = reserveDateString + "就诊" + orderInfo.getDepname();
            // 微信扫码之后,微信显示的内容
            paramMap.put("body", body);
            paramMap.put("out_trade_no", orderInfo.getOutTradeNo());
            // TODO: 上线时使用这行代码
            //paramMap.put("total_fee", orderInfo.getAmount().multiply(new BigDecimal("100")).longValue() + "");
            // 支付金额-测试使用0.01
            paramMap.put("total_fee", "1");
            // 客户端ip地址
            paramMap.put("spbill_create_ip", "127.0.0.1");
            // 回调地址
            paramMap.put("notify_url", "http://guli.shop/api/order/weixinPay/weixinNotify");
            // 支付二维码的类型-NATIVE是带金额
            paramMap.put("trade_type", "NATIVE");

            // 4.发送httpclient请求,请求固定微信接口
            HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder");
            // 设置并传递参数,以xml格式,根据商户key签名
            client.setXmlParam(WXPayUtil.generateSignedXml(paramMap, WxPayProperties.PARTNERKEY));
            // 支持https请求
            client.setHttps(true);
            // 发送请求
            client.post();

            // 5.得到微信接口返回的xml数据,转换为map集合
            String xml = client.getContent();
            Map<String, String> resultMap = WXPayUtil.xmlToMap(xml);

            // 6.封装返回结果集
            HashMap<String, Object> map = new HashMap<>(16);
            map.put("orderId", orderId);
            map.put("totalFee", orderInfo.getAmount());
            // 状态码
            String resultCode = resultMap.get("result_code");
            map.put("resultCode", resultCode);
            // 二维码地址
            map.put("codeUrl", resultMap.get("code_url"));

            // 7.存入Redis中,2小时过期,可采取2小时未支付取消订单
            if (!StringUtils.isEmpty(resultCode)) {
                redisTemplate.opsForValue().set(String.valueOf(orderId), map, 1, TimeUnit.HOURS);
            }
            return map;
        } catch (Exception e) {
            throw new YyghException(20001, "生成二维码失败");
        }
    }

    @Override
    public Map<String, String> queryPayStatus(Long orderId, String paymentType) {
        try {
            // 1.查询订单信息
            OrderInfo orderInfo = orderInfoService.getOrderInfo(orderId);

            // 2.使用map集合,封装微信接口需要的数据
            Map<String, String> paramMap = new HashMap<>();
            // 公众号id
            paramMap.put("appid", WxPayProperties.APPID);
            // 商户号
            paramMap.put("mch_id", WxPayProperties.PARTNER);
            // 交易流水号
            paramMap.put("out_trade_no", orderInfo.getOutTradeNo());
            // 随机字符串
            paramMap.put("nonce_str", WXPayUtil.generateNonceStr());

            // 3.调用微信查询支付状态接口
            HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery");
            // 设置并传递参数,以xml格式,根据商户key签名
            client.setXmlParam(WXPayUtil.generateSignedXml(paramMap, WxPayProperties.PARTNERKEY));
            // 支持https请求
            client.setHttps(true);
            // 发送请求
            client.post();

            // 4.得到微信接口返回的xml格式数据,转换为map返回
            String xml = client.getContent();
            Map<String, String> resultMap = WXPayUtil.xmlToMap(xml);
            return resultMap;
        } catch (Exception e) {
            throw new YyghException(20001, "支付失败");
        }
    }

    @Override
    public Boolean refund(Long orderId) {
        // 1.如果没有支付记录不允许退款
        PaymentInfo paymentInfo = this.paymentInfoService.getOne(Wrappers.lambdaQuery(PaymentInfo.class)
                .eq(PaymentInfo::getOrderId, orderId));
        if (ObjectUtils.isEmpty(paymentInfo)) {
            return false;
        }

        // 2.有支付记录就保存退款记录
        RefundInfo refundInfo = this.refundInfoService.saveRefundInfo(paymentInfo);

        // 2.封装微信退款需要的参数,使用map集合
        Map<String, String> paramMap = new HashMap<>(8);
        // 公众号ID
        paramMap.put("appid", WxPayProperties.APPID);
        //商户编号
        paramMap.put("mch_id", WxPayProperties.PARTNER);
        // 生成随机数
        paramMap.put("nonce_str", WXPayUtil.generateNonceStr());
        //微信订单号
        paramMap.put("transaction_id", paymentInfo.getTradeNo());
        //商户订单编号
        paramMap.put("out_trade_no", paymentInfo.getOutTradeNo());
        //商户退款单号
        paramMap.put("out_refund_no", "tk" + paymentInfo.getOutTradeNo());
        // TODO: 上线时使用下面两行代码
        // paramMap.put("total_fee",paymentInfo.getTotalAmount().multiply(new BigDecimal("100")).longValue()+"");
        // paramMap.put("refund_fee",paymentInfo.getTotalAmount().multiply(new BigDecimal("100")).longValue()+"");
        // 可退款金额
        paramMap.put("total_fee", "1");
        // 退款金额, <=可退款金额
        paramMap.put("refund_fee", "1");

        // 3.发送httpclient请求,请求微信接口
        HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/secapi/pay/refund");
        try {
            // 设置并传递参数,以xml格式,根据商户key签名
            client.setXmlParam(WXPayUtil.generateSignedXml(paramMap, WxPayProperties.PARTNERKEY));
            client.setHttps(true);
            // 设置证书
            client.setCert(true);
            // 证书密码是商户号
            client.setCertPassword(WxPayProperties.PARTNER);
            client.post();

            // 得到微信接口返回的数据,转换为Map
            String xml = client.getContent();
            Map<String, String> resultMap = WXPayUtil.xmlToMap(xml);

            if (null != resultMap && WXPayConstants.SUCCESS.equalsIgnoreCase(resultMap.get("result_code"))) {
                // 修改退款记录为已退款
                refundInfo.setCallbackTime(new Date());
                refundInfo.setTradeNo(resultMap.get("refund_id"));
                refundInfo.setRefundStatus(RefundStatusEnum.REFUND.getStatus());
                refundInfo.setCallbackContent(JSONObject.toJSONString(resultMap));
                refundInfoService.updateById(refundInfo);
                return true;
            }
            return false;
        } catch (Exception e) {
            throw new YyghException(20001, "退款失败");
        }
    }
}
